package org.javaforever.gatescore.core;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.javaforever.gatescore.complexverb.Assign;
import org.javaforever.gatescore.complexverb.DomainFieldVerb;
import org.javaforever.gatescore.complexverb.FrontTwinsVerb;
import org.javaforever.gatescore.complexverb.ListMyActive;
import org.javaforever.gatescore.complexverb.ListMyAvailableActive;
import org.javaforever.gatescore.complexverb.Revoke;
import org.javaforever.gatescore.exception.ValidateException;
import org.javaforever.gatescore.utils.StringUtil;
import org.javaforever.gatescore.verb.Activate;
import org.javaforever.gatescore.verb.ActivateAll;
import org.javaforever.gatescore.verb.Add;
import org.javaforever.gatescore.verb.Clone;
import org.javaforever.gatescore.verb.CloneAll;
import org.javaforever.gatescore.verb.Delete;
import org.javaforever.gatescore.verb.DeleteAll;
import org.javaforever.gatescore.verb.Export;
import org.javaforever.gatescore.verb.ExportPDF;
import org.javaforever.gatescore.verb.ExportPPT;
import org.javaforever.gatescore.verb.ExportWord;
import org.javaforever.gatescore.verb.FilterExcel;
import org.javaforever.gatescore.verb.FilterPDF;
import org.javaforever.gatescore.verb.FilterPPT;
import org.javaforever.gatescore.verb.FilterWord;
import org.javaforever.gatescore.verb.FindById;
import org.javaforever.gatescore.verb.FindByName;
import org.javaforever.gatescore.verb.ListActive;
import org.javaforever.gatescore.verb.ListAll;
import org.javaforever.gatescore.verb.ListAllByPage;
import org.javaforever.gatescore.verb.SearchByFieldsByPage;
import org.javaforever.gatescore.verb.SearchByName;
import org.javaforever.gatescore.verb.SoftDelete;
import org.javaforever.gatescore.verb.SoftDeleteAll;
import org.javaforever.gatescore.verb.Toggle;
import org.javaforever.gatescore.verb.ToggleOne;
import org.javaforever.gatescore.verb.Update;
import org.javaforever.gatescore.vue.ElementUIEnumGridPage;
import org.javaforever.gatescore.vue.ElementUIGridPage;
import org.javaforever.gatescore.vue.ElementUIMtmPage;
import org.javaforever.gatescore.vue.ElementUIPage;
import org.javaforever.gatescore.vue.VueAPIJs;

public class FrontPrism  implements Comparable<FrontPrism>,Cloneable,Serializable {
	private static final long serialVersionUID = 3017829512307723077L;
	protected String standardName;
	protected FrontDomain domain;
	protected String prismComment;
	protected List<FrontUtil> utils = new ArrayList<FrontUtil>();
	protected List<FrontController> controllers = new ArrayList<FrontController>();
	protected String folderPath = "D:/JerryWork/Infinity/testFiles/";
	protected String packageToken;
	protected Set<FrontVerb> verbs = new TreeSet<>();
	protected Set<FrontTwinsVerb> twinsverbs = new TreeSet<FrontTwinsVerb>();
	protected String label;
	protected Set<Pair> manyToManySlaveNames = new TreeSet<Pair>();
	protected Set<FrontManyToMany> manyToManies = new TreeSet<FrontManyToMany>();
	protected List<FrontDomain> projectDomains = new ArrayList<FrontDomain>();
	protected String title = "";
	protected String subTitle = "";
	protected String footer = "";
	protected Set<DomainFieldVerb> domianFieldVerbs = new TreeSet<>();
	protected Set<ElementUIPage> pages = new TreeSet<>();
	protected VueAPIJs api;
	
	public void generatePrismFromDomain() throws ValidateException, Exception {
		if (this.domain != null) {
			if (this.getPackageToken() != null) {
				this.domain.setPackageToken(packageToken);
			}

			FrontVerb listAll = new ListAll(this.domain);
			FrontVerb update = new Update(this.domain);
			FrontVerb delete = new Delete(this.domain);
			FrontVerb add = new Add(this.domain);
			FrontVerb softdelete = new SoftDelete(this.domain);
			FrontVerb findbyid = new FindById(this.domain);
			FrontVerb findbyname = new FindByName(this.domain);
			FrontVerb searchbyname = new SearchByName(this.domain);
			FrontVerb listactive = new ListActive(this.domain);
			FrontVerb listAllByPage = new ListAllByPage(this.domain);
			FrontVerb deleteAll = new DeleteAll(this.domain);
			FrontVerb softDeleteAll = new SoftDeleteAll(this.domain);
			FrontVerb toggle = new Toggle(this.domain);
			FrontVerb toggleOne = new ToggleOne(this.domain);
			FrontVerb searchByFieldsByPage = new SearchByFieldsByPage(this.domain);
			FrontVerb activate = new Activate(this.domain);
			FrontVerb activateAll = new ActivateAll(this.domain);
			FrontVerb export = new Export(this.domain);
			FrontVerb clone = new Clone(this.domain);
			FrontVerb cloneAll = new CloneAll(this.domain);
			FrontVerb exportExcel = new Export(this.domain);
			FrontVerb exportPDF = new ExportPDF(this.domain);
			FrontVerb exportWord = new ExportWord(this.domain);
			FrontVerb exportPPT = new ExportPPT(this.domain);
			FrontVerb filterExcel = new FilterExcel(this.domain);
			FrontVerb filterPDF = new FilterPDF(this.domain);
			FrontVerb filterWord = new FilterWord(this.domain);
			FrontVerb filterPPT = new FilterPPT(this.domain);

			this.addVerb(listAll);
			this.addVerb(update);
			this.addVerb(delete);
			this.addVerb(add);
			this.addVerb(softdelete);
			this.addVerb(findbyid);
			this.addVerb(findbyname);
			this.addVerb(searchbyname);
			this.addVerb(listactive);
			this.addVerb(listAllByPage);
			this.addVerb(deleteAll);
			this.addVerb(softDeleteAll);
			this.addVerb(toggle);
			this.addVerb(toggleOne);
			this.addVerb(searchByFieldsByPage);
			this.addVerb(activate);
			this.addVerb(activateAll);
			this.addVerb(export);
			this.addVerb(clone);
			this.addVerb(cloneAll);
			this.addVerb(exportExcel);
			this.addVerb(exportPDF);
			this.addVerb(exportWord);
			this.addVerb(exportPPT);
			this.addVerb(filterExcel);
			this.addVerb(filterPDF);
			this.addVerb(filterWord);
			this.addVerb(filterPPT);
			
			this.api = new VueAPIJs(this.domain);
			this.api.setTwinsverbs(this.twinsverbs);
			
			if (this.domain.manyToManies != null && this.domain.manyToManies.size() > 0) {
				for (FrontManyToMany mtm : this.domain.manyToManies) {
					String slaveName = mtm.getManyToManySalveName();
					String masterName = this.domain.getStandardName();
					System.out.println(slaveName+":"+masterName);
					if (listContainsDomain(this.projectDomains, masterName)
							&& listContainsDomain(this.projectDomains, slaveName)) {
						FrontDomain myslave = (FrontDomain)lookupDoaminInList(this.projectDomains, slaveName).clone();
						if (!StringUtil.isBlank(mtm.getSlaveAlias())){
							myslave.setAlias(mtm.getSlaveAlias());
							myslave.setAliasLabel(mtm.getSlaveAliasLabel());
						}
						FrontManyToMany mymtm = new FrontManyToMany(lookupDoaminInList(this.projectDomains, masterName),
								myslave,mtm.getMasterValue(),mtm.getValues());	
						mymtm.setSlaveAlias(myslave.getAlias());
						mymtm.setSlaveAliasLabel(mtm.getSlaveAliasLabel());
						mymtm.setStandardName("Link"+StringUtil.capFirst(masterName)+StringUtil.capFirst(myslave.getAliasOrName()));
						mymtm.setValues(mtm.getValues());
						this.manyToManies.add(mymtm);
						Assign assign = new Assign(mymtm);
						Revoke revoke = new Revoke(mymtm);
						ListMyActive lma = new ListMyActive(mymtm);
						ListMyAvailableActive lmaa = new ListMyAvailableActive(mymtm);
						addTwinsVerb(assign);
						addTwinsVerb(revoke);
						addTwinsVerb(lma);
						addTwinsVerb(lmaa);						
					} else {
						ValidateInfo validateInfo = new ValidateInfo();
						validateInfo.addCompileError("棱柱" + this.getText() + "多对多设置有误。");
						ValidateException em = new ValidateException(validateInfo);
						throw em;
					}
				}
			}
		}
	}
	
	public String getText() {
		if (this.label != null && !this.label.equals(""))
			return this.label;
		else
			return this.standardName;
	}

	protected Boolean listContainsDomain(List<FrontDomain> list, String domainName) {
		for (FrontDomain d : list) {
			if (d.getStandardName().equals(domainName))
				return true;
		}
		return false;
	}

	
	protected FrontDomain lookupDoaminInSet(Set<FrontDomain> set, String domainName) {
		for (FrontDomain d : set) {
			if (d.getStandardName().equals(domainName))
				return d;
		}
		return null;
	}
	
	protected FrontDomain lookupDoaminInList(List<FrontDomain> list, String domainName) {
		for (FrontDomain d : list) {
			if (d.getStandardName().equals(domainName))
				return d;
		}
		return null;
	}
	
	public ValidateInfo validate() {
		ValidateInfo info = new ValidateInfo();
		info.setSuccess(true);
		return info;
	}
	
	public  void writeToFile(String filePath, String content) throws Exception{
		File f = new File(filePath);
		if (!f.getParentFile().exists()) {
			f.getParentFile().mkdirs();
		}
		f.createNewFile();
		try (Writer fw = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(f.getAbsolutePath()),"UTF-8"))){			
	        fw.write(content,0,content.length());
		}
	}
	
	public void generatePrismFiles() throws ValidateException {
		ValidateInfo info = this.validate();
		if (info.success(false) == false) {
			ValidateException e = new ValidateException(info);
			throw e;
		}
		try {
			ElementUIGridPage etp;
			if (this.domain instanceof FrontEnum) {
				etp = new ElementUIEnumGridPage((FrontEnum)this.domain);
				etp.setVerbs(this.verbs);
				etp.setTwinsverbs(this.twinsverbs);
			}else {
				etp = new ElementUIGridPage(this.domain);
				etp.setVerbs(this.verbs);
				etp.setTwinsverbs(this.twinsverbs);
			}
			writeToFile(folderPath + "src/views/pages/" + this.domain.getPlural().toLowerCase() + ".vue",
					etp.generateStatementList().getContent());
			
			if (this.api == null) {
				this.api = new VueAPIJs(this.domain);
				this.api.setTwinsverbs(this.twinsverbs);
			}
			writeToFile(folderPath + "src/api/" + this.api.getFileName(),
					this.api.generateStatementList().getContent());
				
			for (FrontManyToMany mtm : this.manyToManies) {
				if (!mtm.getMaster().isLegacy()&&!mtm.getSlave().isLegacy()) { 
					ElementUIMtmPage mtmp = new ElementUIMtmPage(mtm);
					writeToFile(folderPath + "src/views/pages/" + mtm.getStandardName().toLowerCase() + ".vue",
							mtmp.generateStatementList().getContent());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public int compareTo(FrontPrism o) {
		return this.standardName.compareTo(o.getStandardName());
	}
	public String getStandardName() {
		return standardName;
	}
	public void setStandardName(String standardName) {
		this.standardName = standardName;
	}
	public String getPrismComment() {
		return prismComment;
	}
	public void setPrismComment(String prismComment) {
		this.prismComment = prismComment;
	}
	public List<FrontUtil> getUtils() {
		return utils;
	}
	public void setUtils(List<FrontUtil> utils) {
		this.utils = utils;
	}
	public List<FrontController> getControllers() {
		return controllers;
	}
	public void setControllers(List<FrontController> controllers) {
		this.controllers = controllers;
	}
	public String getFolderPath() {
		return folderPath;
	}
	public void setFolderPath(String folderPath) {
		this.folderPath = folderPath;
	}
	public String getPackageToken() {
		return packageToken;
	}
	public void setPackageToken(String packageToken) {
		this.packageToken = packageToken;
	}
	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
	public Set<Pair> getManyToManySlaveNames() {
		return manyToManySlaveNames;
	}
	public void setManyToManySlaveNames(Set<Pair> manyToManySlaveNames) {
		this.manyToManySlaveNames = manyToManySlaveNames;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getSubTitle() {
		return subTitle;
	}
	public void setSubTitle(String subTitle) {
		this.subTitle = subTitle;
	}
	public String getFooter() {
		return footer;
	}
	public void setFooter(String footer) {
		this.footer = footer;
	}
	public Set<FrontManyToMany> getManyToManies() {
		return manyToManies;
	}
	public void setManyToManies(Set<FrontManyToMany> manyToManies) {
		this.manyToManies = manyToManies;
	}
	public FrontDomain getDomain() {
		return domain;
	}
	public void setDomain(FrontDomain domain) {
		this.domain = domain;
	}
	public Set<FrontVerb> getVerbs() {
		return verbs;
	}
	public void setVerbs(Set<FrontVerb> verbs) {
		this.verbs = verbs;
	}
	public void addVerb(FrontVerb verb) {
		this.verbs.add(verb);
	}

	public Set<FrontTwinsVerb> getTwinsverbs() {
		return twinsverbs;
	}

	public void setTwinsverbs(Set<FrontTwinsVerb> twinsverbs) {
		this.twinsverbs = twinsverbs;
	}
	
	public void addTwinsVerb(FrontTwinsVerb twinsVerb) {
		this.twinsverbs.add(twinsVerb);
	}

	public Set<DomainFieldVerb> getDomianFieldVerbs() {
		return domianFieldVerbs;
	}

	public void setDomianFieldVerbs(Set<DomainFieldVerb> domianFieldVerbs) {
		this.domianFieldVerbs = domianFieldVerbs;
	}
	
	public void addDomianFieldVerb(DomainFieldVerb dfv) {
		this.domianFieldVerbs.add(dfv);
	}

	public Set<ElementUIPage> getPages() {
		return pages;
	}

	public void setPages(Set<ElementUIPage> pages) {
		this.pages = pages;
	}
	
	public void addPage(ElementUIPage page) {
		this.pages.add(page);
	}
	
	public void removePage(String pageName) {
		for (ElementUIPage p:this.pages) {
			if (pageName.equals(p.getStandardName())) this.pages.remove(p);
		}
	}
	
	public void removeVerb(String verbName) {
		Set<FrontVerb> results = new TreeSet<>();
		for (FrontVerb v:this.verbs) {
			if (verbName.equals(v.getVerbName())) continue;
			else results.add(v);
		}
		this.verbs = results;
	}

	public VueAPIJs getApi() {
		return api;
	}

	public void setApi(VueAPIJs api) {
		this.api = api;
	}

	public List<FrontDomain> getProjectDomains() {
		return projectDomains;
	}

	public void setProjectDomains(List<FrontDomain> projectDomains) {
		this.projectDomains = projectDomains;
	}
}
